'''
序列化器：序列化器主要与模型类交互，进行序列化，反序列化等
1，序列化：将模型转化成json数据
    1，定义序列化器（XxxSerializer一般是模型类名+Serializer）继承serializers.ModelSerializer或Serializer
    2，定义序列化器中的字段（字段名与模型类字段名一致，字段可多可少）
    3，多里面关联序列化一（外键），一里面关联序列化多（多里面模型名小写_set）
    4，一里面关联序列化多时，需要指定关联字段many=True
    5，将要序列化的模型或查询集传给序列化器类的instance参数时，如果传查询集多many=True
2，反序列化：

'''

import re
from django_redis import get_redis_connection # 通过配置文件连接redis的包
from rest_framework import serializers # 指向rest_framework框架中的的哪个序列化类要被继承（ModelSerializer）
# JWT 的token认证导包(rest_framework_jwt别搞错成rest_framework ！)
from rest_framework_jwt.settings import api_settings
from celery_tasks.email.tasks import send_verify_email # 导入邮箱任务发送验证邮箱的方法

from .models import User, Address, Area # 导入模型类，进行序列化操作



# 用户注册登录验证
class CreateUserSerializer(serializers.ModelSerializer):
    """
    创建用户序列化器
    """
    password2 = serializers.CharField(label='确认密码', write_only=True)
    sms_code = serializers.CharField(label='短信验证码', write_only=True)
    allow = serializers.CharField(label='同意协议', write_only=True)
    # token序列化
    token = serializers.CharField(label='登录状态token', read_only=True) # 必须为read_only，write_only报错

    class Meta:
        model = User
        fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token')  # 每个字段需要加入才能序列化
        extra_kwargs = {
            'username': {
                'min_length': 5,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许5-20个字符的用户名',
                    'max_length': '仅允许5-20个字符的用户名',
                }
            },
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许8-20个字符的密码',
                    'max_length': '仅允许8-20个字符的密码',
                }
            }
        }

    def validate_mobile(self, value):
        """验证手机号"""
        if not re.match(r'^1[3-9]\d{9}$', value):
            raise serializers.ValidationError('手机号格式错误')
        return value

    def validate_allow(self, value):
        """检验用户是否同意协议"""
        if value != 'true':
            raise serializers.ValidationError('请同意用户协议')
        return value

    def validate(self, data):
        # 判断两次密码
        if data['password'] != data['password2']:
            raise serializers.ValidationError('两次密码不一致')

        # 判断短信验证码
        redis_conn = get_redis_connection('verify_codes') # 通过配置文件中redis缓存的配置，找到相应的redis数据库缓存数据
        mobile = data['mobile']
        real_sms_code = redis_conn.get('sms_%s' % mobile)
        if real_sms_code is None:
            raise serializers.ValidationError('无效的短信验证码')
        if data['sms_code'] != real_sms_code.decode():
            raise serializers.ValidationError('短信验证码错误')

        return data

    def create(self, validated_data):
        """
        创建用户
        """
        # 移除数据库模型类中不存在的属性
        del validated_data['password2']
        del validated_data['sms_code']
        del validated_data['allow']
        user = super().create(validated_data)

        # 调用django的认证系统加密密码
        user.set_password(validated_data['password'])
        user.save()

        # print("即将生成token...")
        # 构造一个token发送给浏览器
        # 指定jwt的载荷和头部信息构造的方法
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

        # 用户信息放入载荷
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        # 把token添加到这个序列化中，用于返回
        user.token = token
        # print(token)
        # print("运行序列化完成！")
        return user

# 用户个人信息详情（邮箱验证）
class UserDetailSerializer(serializers.ModelSerializer):
    """
    用户详细信息序列化器
    """
    class Meta:
        model = User
        fields = ('id', 'username', 'mobile', 'email', 'email_active')


# 验证用户提交的邮箱信息的序列化器
class EmailSerializer(serializers.ModelSerializer):
    """
    邮箱序列化器
    """
    class Meta:
        model = User
        fields = ('id', 'email')

        # 检验邮箱是否反序列化操作（True：反序列化）
        extra_kwargs = {
            'email': {
                'required': True
            }
        }

    # instance：需要序列化的数据  validated_data：用来获取反序列化验证通过后的数据
    def update(self, instance, validated_data):
        # 先查询，再修改，最后保存邮箱
        instance.email = validated_data['email']
        instance.save()

        # 生成验证链接（调用了User模型类的生成邮箱连接的方法）
        verify_url = instance.generate_email_verify_url()
        # 打印测试链接
        # print(verify_url)
        # 发送验证邮件
        send_verify_email.delay(instance.email, verify_url)
        return instance


# 行政区划信息序列化器（省序列化器）
class AreaSerializer(serializers.ModelSerializer):
    """
    行政区划信息序列化器(省)
    """
    class Meta:
        model = Area
        fields = ('id', 'name')

# 子行政区划信息序列化器（市/区的序列化器）
class SubAreaSerializer(serializers.ModelSerializer):
    """
    子行政区划信息序列化器
    """
    # 调用省的序列化器 子是多的一方，须指定：many=True，read_only=True必须返回
    subs = AreaSerializer(many=True, read_only=True)

    class Meta:
        model = Area
        fields = ('id', 'name', 'subs')


class UserAddressSerializer(serializers.ModelSerializer):
    """
    用户地址序列化器
    """
    # 序列化 省市区的名称和id进行校验  StringRelatedField--可以指定为字符串类型的序列化，不需要去指定长度
    province = serializers.StringRelatedField(read_only=True)
    city = serializers.StringRelatedField(read_only=True)
    district = serializers.StringRelatedField(read_only=True)
    province_id = serializers.IntegerField(label='省ID', required=True)
    city_id = serializers.IntegerField(label='市ID', required=True)
    district_id = serializers.IntegerField(label='区ID', required=True)

    class Meta:
        model = Address
        # 排除指定的字段
        exclude = ('user', 'is_deleted', 'create_time', 'update_time')

    def create(self, validated_data):
        """
        验证手机号--保存当前登录用户的数据--添加到用户地址中的user
        """
        # 验证手机号
        if not re.match(r'^1[3-9]\d{9}$', validated_data['mobile']):
            raise serializers.ValidationError('手机号格式错误')

        # 保存当前登录用户的数据--添加到用户地址中的user
        validated_data['user'] = self.context['request'].user
        return super().create(validated_data)


class AddressTitleSerializer(serializers.ModelSerializer):
    """
    地址标题
    """

    class Meta:
        model = Address
        fields = ('title',)